package com.neusoft.lohas.common.filter;

import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 用于重写http头信息
 * @author jackie.liu
 * @version 2014-12-24
 */
public class HttpHeadFilter extends OncePerRequestFilter {

	/** Default method parameter: {@code _method} */
	public static final String DEFAULT_METHOD_PARAM = "_method";
	protected Logger loggerCommon = Logger.getLogger(HttpHeadFilter.class);

	public static final String MODIFY_SINCE = "If_Modified_Since";

	private String ifModifySince = MODIFY_SINCE;
	private String methodParam = DEFAULT_METHOD_PARAM;

	/**
	 * Set the parameter name to look for HTTP methods.
	 * @see #DEFAULT_METHOD_PARAM
	 */
	public void setMethodParam(String methodParam) {
		Assert.hasText(methodParam, "'methodParam' must not be empty");
		this.methodParam = methodParam;
	}

	public void setIfModifySince(String ifModifySince) {
		this.ifModifySince = ifModifySince;
	}

	private String encoding;

	private boolean forceEncoding = false;

	/**
	 * Set the encoding to use for requests. This encoding will be passed into a
	 * {@link javax.servlet.http.HttpServletRequest#setCharacterEncoding} call.
	 * <p>Whether this encoding will override existing request encodings
	 * (and whether it will be applied as default response encoding as well)
	 * depends on the {@link #setForceEncoding "forceEncoding"} flag.
	 */
	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	/**
	 * Set whether the configured {@link #setEncoding encoding} of this filter
	 * is supposed to override existing request and response encodings.
	 * <p>Default is "false", i.e. do not modify the encoding if
	 * {@link javax.servlet.http.HttpServletRequest#getCharacterEncoding()}
	 * returns a non-null value. Switch this to "true" to enforce the specified
	 * encoding in any case, applying it as default response encoding as well.
	 * <p>Note that the response encoding will only be set on Servlet 2.4+
	 * containers, since Servlet 2.3 did not provide a facility for setting
	 * a default response encoding.
	 */
	public void setForceEncoding(boolean forceEncoding) {
		this.forceEncoding = forceEncoding;
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		if (this.encoding != null
				&& (this.forceEncoding || request.getCharacterEncoding() == null)) {
			request.setCharacterEncoding(this.encoding);
			if (this.forceEncoding) {
				response.setCharacterEncoding(this.encoding);
			}
		}
		String paramValue = request.getParameter(this.methodParam);
		String lastModify = request.getParameter(this.ifModifySince);
		if (// "POST".equals(request.getMethod()
		StringUtils.hasLength(paramValue)) {
			String method = paramValue.toUpperCase(Locale.ENGLISH);
			HttpServletRequest wrapper = new HttpMethodRequestWrapper(request,
					method);

			loggerCommon.info(getLogInfo(wrapper));
			filterChain.doFilter(wrapper, response);
		}
		else if ("GET".equals(request.getMethod())
				&& StringUtils.hasLength(lastModify)) {
			HttpMethodRequestWrapper wrapper = new HttpMethodRequestWrapper(
					request,
					"GET");
			wrapper.addParam("If-Modified-Since", lastModify);
			loggerCommon.info(getLogInfo(wrapper));
			filterChain.doFilter(wrapper, response);

		}
		else {
			loggerCommon.info(getLogInfo(request));
			filterChain.doFilter(request, response);

		}
	}

	/**
	 * Simple {@link HttpServletRequest} wrapper that returns the supplied method for
	 * {@link HttpServletRequest#getMethod()}.
	 */
	private static class HttpMethodRequestWrapper extends
			HttpServletRequestWrapper {

		private final String method;
		private final Map<String, String> map = new HashMap<String, String>(16);

		public HttpMethodRequestWrapper(HttpServletRequest request,
				String method) {
			super(request);
			this.method = method;
		}

		@Override
		public String getMethod() {
			return this.method;
		}

		@Override
		public String getHeader(String name) {
			String result = map.get(name);
			if (result == null)
				result = super.getHeader(name);
			return result;
		}

		@Override
		public Enumeration getHeaders(final String name) {
			Enumeration enumeration = null;
			if ("If-Modified-Since".equals(name))
				enumeration = new Enumeration<String>() {
					int count = 1;

					@Override
					public boolean hasMoreElements() {
						return count-- > 0;
					}

					@Override
					public String nextElement() {
						return map.get(name);
					}
				};
			else {
				enumeration = super.getHeaders(name);
			}
			return enumeration;
		}

		@Override
		public Enumeration getHeaderNames() {
			return super.getHeaderNames();
		}

		public String addParam(String key, String value) {
			return map.put(key, value);
		}
	}

	public String getLogInfo(HttpServletRequest request)
	{
		Map<String, Object> requestData = new HashMap<String, Object>();
		Enumeration<String> parameterNames = request.getParameterNames();
		StringBuffer paramString = new StringBuffer("[ remote Ip : "
				+ request.getRemoteAddr() + " ] " + " [ request Path: "
				+ request.getRequestURI() + " ] " + " [ request method: "
				+ request.getMethod() + " ] ");
		requestData.put("remote Ip", request.getRemoteAddr());
		requestData.put("request Path", request.getRequestURI());
		requestData.put("request method", request.getMethod());
		String parameName = null;
		while (parameterNames.hasMoreElements()) {
			parameName = parameterNames.nextElement();
			if (parameName.equals("password"))
				continue;
			paramString.append(" [ " + parameName + " : "
					+ request.getParameter(parameName) + " ] ");
			requestData.put(parameName, request.getParameter(parameName));
		}
		return paramString.toString();
	}
	
}
